Инвесторы из фонда «Shut Up and Take My Money» решили попробовать себя в новой области и открыть заведение общественного питания в Москве. Заказчики ещё не знают, что это будет за место: кафе, ресторан, пиццерия, паб или бар, — и какими будут расположение, меню и цены.
Для начала они просят подготовить исследование рынка общественного питания Москвы, найти интересные особенности и презентовать полученные результаты, которые в будущем помогут в выборе подходящего инвесторам места.
Подготовим информативную и лаконичную презентацию. Мне доступен датасет с заведениями общественного питания Москвы, составленный на основе данных сервисов Яндекс Карты и Яндекс Бизнес на лето 2022 года.
Файл: /datasets/moscow_places.csv
avg_bill — строка, которая хранит среднюю стоимость заказа в виде диапазона, например:
middle_avg_bill — число с оценкой среднего чека, которое указано только для значений из столбца avg_bill, начинающихся с
подстроки «Средний счёт»:
Если в строке указан ценовой диапазон из двух значений, в столбец войдёт медиана этих двух значений.
Если в строке указано одно число — цена без диапазона, то в столбец войдёт это число.
Если значения нет или оно не начинается с подстроки «Средний счёт», то в столбец ничего не войдёт.
middle_coffee_cup — число с оценкой одной чашки капучино, которое указано только для значений из столбца avg_bill, начинающихся с подстроки «Цена одной чашки капучино»: Если в строке указан ценовой диапазон из двух значений, в столбец войдёт медиана этих двух значений. Если в строке указано одно число — цена без диапазона, то в столбец войдёт это число. Если значения нет или оно не начинается с подстроки «Цена одной чашки капучино», то в столбец ничего не войдёт.
chain — число, выраженное 0 или 1, которое показывает, является ли заведение сетевым (для маленьких сетей могут
встречаться ошибки):
district — административный район, в котором находится заведение, например Центральный административный округ;
seats — количество посадочных мест.
Шаг 1. Загрузка данных и изучение информации
Шаг 2. Выполнение предобработки данных
Предобработка данных:
Шаг 3. Анализ данных
Шаг 4. Детализация исследования: открытие кофейни
Основателям фонда «Shut Up and Take My Money» не даёт покоя успех сериала «Друзья». Их мечта — открыть такую же крутую и доступную, как «Central Perk», кофейню в Москве. Будем считать, что заказчики не боятся конкуренции в этой сфере, ведь кофеен в больших городах уже достаточно. Попробуйте определить, осуществима ли мечта клиентов.
Необходимо ответить на вопросы:
Постройте визуализации. Попробуйте дать рекомендацию для открытия нового заведения. Это творческое задание: здесь нет правильного или неправильного ответа, но ваше решение должно быть чем-то обосновано. Объяснить свою рекомендацию можно текстом с описанием или маркерами на географической карте.
Шаг 5. Подготовка презентации
Подготовьте презентацию исследования для инвесторов. Отвечая на вопросы о московском общепите, вы уже построили много диаграмм, и помещать каждую из них в презентацию не нужно. Выберите важные тезисы и наблюдения, которые могут заинтересовать заказчиков. Для создания презентации используйте любой удобный инструмент, но отправить презентацию нужно обязательно в формате PDF. Приложите ссылку на презентацию в markdown-ячейке в формате: Презентация: <ссылка на облачное хранилище с презентацией>
# импортируем необходимые для работы библиотеки
import pandas as pd
import numpy as np
import numpy as npмsns
import datetime as dt
import seaborn as sns
pd.set_option('display.max_columns', 0)
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objects as go
import warnings
warnings.filterwarnings("ignore")
# импортируем карту и хороплет
from folium import Map, Choropleth, Marker
# импортируем кластер
from folium.plugins import MarkerCluster
import matplotlib
%config InlineBackend.figure_format = 'retina' # используем для более красивых графичков.
matplotlib.style.use('ggplot')
plt.rc('figure', figsize=(15, 6))
# для исключения ошибок загрузки используем функцию try и expert
try:
df = pd.read_csv('/datasets/moscow_places.csv')
except:
df = pd.read_csv('https://code.s3.yandex.net/datasets/moscow_places.csv')
# напишем функцию для обзора данных
def df_review(df):
display(df.head())
print('---------------------------------------------------------------------------------------------------------')
print(df.info())
print('---------------------------------------------------------------------------------------------------------')
print(df.describe())
print('---------------------------------------------------------------------------------------------------------')
print(df.isna().sum())
print('---------------------------------------------------------------------------------------------------------')
print(df.duplicated().sum())
df_review(df)
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | WoWфли | кафе | Москва, улица Дыбенко, 7/1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.878494 | 37.478860 | 5.0 | NaN | NaN | NaN | NaN | 0 | NaN |
| 1 | Четыре комнаты | ресторан | Москва, улица Дыбенко, 36, корп. 1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.875801 | 37.484479 | 4.5 | выше среднего | Средний счёт:1500–1600 ₽ | 1550.0 | NaN | 0 | 4.0 |
| 2 | Хазри | кафе | Москва, Клязьминская улица, 15 | Северный административный округ | пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00... | 55.889146 | 37.525901 | 4.6 | средние | Средний счёт:от 1000 ₽ | 1000.0 | NaN | 0 | 45.0 |
| 3 | Dormouse Coffee Shop | кофейня | Москва, улица Маршала Федоренко, 12 | Северный административный округ | ежедневно, 09:00–22:00 | 55.881608 | 37.488860 | 5.0 | NaN | Цена чашки капучино:155–185 ₽ | NaN | 170.0 | 0 | NaN |
| 4 | Иль Марко | пиццерия | Москва, Правобережная улица, 1Б | Северный административный округ | ежедневно, 10:00–22:00 | 55.881166 | 37.449357 | 5.0 | средние | Средний счёт:400–600 ₽ | 500.0 | NaN | 1 | 148.0 |
---------------------------------------------------------------------------------------------------------
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 8406 entries, 0 to 8405
Data columns (total 14 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 name 8406 non-null object
1 category 8406 non-null object
2 address 8406 non-null object
3 district 8406 non-null object
4 hours 7870 non-null object
5 lat 8406 non-null float64
6 lng 8406 non-null float64
7 rating 8406 non-null float64
8 price 3315 non-null object
9 avg_bill 3816 non-null object
10 middle_avg_bill 3149 non-null float64
11 middle_coffee_cup 535 non-null float64
12 chain 8406 non-null int64
13 seats 4795 non-null float64
dtypes: float64(6), int64(1), object(7)
memory usage: 919.5+ KB
None
---------------------------------------------------------------------------------------------------------
lat lng ... chain seats
count 8406.000000 8406.000000 ... 8406.000000 4795.000000
mean 55.750109 37.608570 ... 0.381275 108.421689
std 0.069658 0.098597 ... 0.485729 122.833396
min 55.573942 37.355651 ... 0.000000 0.000000
25% 55.705155 37.538583 ... 0.000000 40.000000
50% 55.753425 37.605246 ... 0.000000 75.000000
75% 55.795041 37.664792 ... 1.000000 140.000000
max 55.928943 37.874466 ... 1.000000 1288.000000
[8 rows x 7 columns]
---------------------------------------------------------------------------------------------------------
name 0
category 0
address 0
district 0
hours 536
lat 0
lng 0
rating 0
price 5091
avg_bill 4590
middle_avg_bill 5257
middle_coffee_cup 7871
chain 0
seats 3611
dtype: int64
---------------------------------------------------------------------------------------------------------
0
# приведем столбец к нижнему регистру
df['name'] = df['name'].str.lower()
df['address'] = df['address'].str.lower()
df.head()
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | wowфли | кафе | москва, улица дыбенко, 7/1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.878494 | 37.478860 | 5.0 | NaN | NaN | NaN | NaN | 0 | NaN |
| 1 | четыре комнаты | ресторан | москва, улица дыбенко, 36, корп. 1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.875801 | 37.484479 | 4.5 | выше среднего | Средний счёт:1500–1600 ₽ | 1550.0 | NaN | 0 | 4.0 |
| 2 | хазри | кафе | москва, клязьминская улица, 15 | Северный административный округ | пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00... | 55.889146 | 37.525901 | 4.6 | средние | Средний счёт:от 1000 ₽ | 1000.0 | NaN | 0 | 45.0 |
| 3 | dormouse coffee shop | кофейня | москва, улица маршала федоренко, 12 | Северный административный округ | ежедневно, 09:00–22:00 | 55.881608 | 37.488860 | 5.0 | NaN | Цена чашки капучино:155–185 ₽ | NaN | 170.0 | 0 | NaN |
| 4 | иль марко | пиццерия | москва, правобережная улица, 1б | Северный административный округ | ежедневно, 10:00–22:00 | 55.881166 | 37.449357 | 5.0 | средние | Средний счёт:400–600 ₽ | 500.0 | NaN | 1 | 148.0 |
df.duplicated(subset=['name', 'address']).sum()
print(len(df))
df = df.drop_duplicates(subset=['name', 'address'])
print(len(df))
8406 8402
Удалили неявные дубликаты.
print('Количество заведений:', df['name'].nunique())
Количество заведений: 5512
df['category'].value_counts()
category кафе 2376 ресторан 2042 кофейня 1413 бар,паб 764 пиццерия 633 быстрое питание 603 столовая 315 булочная 256 Name: count, dtype: int64
Вывод:
Видим пропуски в столбцах:
Пропусков в данных большое количество. Просто удалить их нельзя, т. к. будут искажения в исследовании. Исследуем эти значения и посмотрим чем их можно заменить.
Для дальнейшего исследования нам нужен столбец middle_avg_bill. Значения для этого столбца берутся из avg_bill.
new_avg_bill = df.groupby('avg_bill')['middle_avg_bill'].max().dropna()
display(new_avg_bill.index)
display(new_avg_bill.head())
Index(['Средний счёт:100 ₽', 'Средний счёт:1000 ₽',
'Средний счёт:1000–10000 ₽', 'Средний счёт:1000–1100 ₽',
'Средний счёт:1000–1200 ₽', 'Средний счёт:1000–1300 ₽',
'Средний счёт:1000–1350 ₽', 'Средний счёт:1000–1400 ₽',
'Средний счёт:1000–1500 ₽', 'Средний счёт:1000–1600 ₽',
...
'Средний счёт:от 499 ₽', 'Средний счёт:от 50 ₽',
'Средний счёт:от 500 ₽', 'Средний счёт:от 5000 ₽',
'Средний счёт:от 549 ₽', 'Средний счёт:от 600 ₽',
'Средний счёт:от 650 ₽', 'Средний счёт:от 700 ₽',
'Средний счёт:от 800 ₽', 'Средний счёт:от 850 ₽'],
dtype='object', name='avg_bill', length=555)
avg_bill Средний счёт:100 ₽ 100.0 Средний счёт:1000 ₽ 1000.0 Средний счёт:1000–10000 ₽ 5500.0 Средний счёт:1000–1100 ₽ 1050.0 Средний счёт:1000–1200 ₽ 1100.0 Name: middle_avg_bill, dtype: float64
Отобрали все значения начинающиеся с "Средний счёт:".
Вывод:
Полагаю, что заполнять средними или медианными значениями по названию заведения остальные столбцы некорректно, так как количество мест в сетевых заведениях, в разных районах, может отличаться. Также в мелких сетях, по условию, могут быть ошибки. Оставим пропуски как есть.
Посмотрим на количество сидячих мест
df['seats'].describe()
count 4792.000000 mean 108.361436 std 122.841130 min 0.000000 25% 40.000000 50% 75.000000 75% 140.000000 max 1288.000000 Name: seats, dtype: float64
1288 - очень большое значение, даже для Москвы. Построим boxplot и посмотрим на выбросы.
# построим график boxplot для визуализации выборосов
sns.set_palette('rocket')
sns.boxplot(y='seats', x='category', data=df, hue="category", dodge=False)
sns.set(rc={'figure.figsize':(13,13)})
plt.title('Количество сидячих мест в заведениях')
plt.xlabel('Тип заведения')
plt.ylabel('Количество сидячих мест')
plt.show()
Вывод:
На графике есть выбросы. Скорее всего они связаны с ошибкой при заполнении данных. Установим допустимую границу в 560 мест. Остальное отсечем. Посчитаем количество таких заведений.
print('Количество заведений с числом мест больше 560:', df[df['seats'] > 560]['name'].count())
Количество заведений с числом мест больше 560: 56
Таких заведений меньше 1%, можно их удалить.
df_550 = df[df['seats'] > 560]
df = df.query('index not in @df_550.index')
print('Заведений с числом мест больше 560:', df[df['seats'] > 560]['name'].count())
Заведений с числом мест больше 560: 0
Предобработка данных:
Создадим столбец street с названиями улиц из столбца address
df['street'] = df['address'].str.split(', ').str[1]
df.head()
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | street | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | wowфли | кафе | москва, улица дыбенко, 7/1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.878494 | 37.478860 | 5.0 | NaN | NaN | NaN | NaN | 0 | NaN | улица дыбенко |
| 1 | четыре комнаты | ресторан | москва, улица дыбенко, 36, корп. 1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.875801 | 37.484479 | 4.5 | выше среднего | Средний счёт:1500–1600 ₽ | 1550.0 | NaN | 0 | 4.0 | улица дыбенко |
| 2 | хазри | кафе | москва, клязьминская улица, 15 | Северный административный округ | пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00... | 55.889146 | 37.525901 | 4.6 | средние | Средний счёт:от 1000 ₽ | 1000.0 | NaN | 0 | 45.0 | клязьминская улица |
| 3 | dormouse coffee shop | кофейня | москва, улица маршала федоренко, 12 | Северный административный округ | ежедневно, 09:00–22:00 | 55.881608 | 37.488860 | 5.0 | NaN | Цена чашки капучино:155–185 ₽ | NaN | 170.0 | 0 | NaN | улица маршала федоренко |
| 4 | иль марко | пиццерия | москва, правобережная улица, 1б | Северный административный округ | ежедневно, 10:00–22:00 | 55.881166 | 37.449357 | 5.0 | средние | Средний счёт:400–600 ₽ | 500.0 | NaN | 1 | 148.0 | правобережная улица |
Создадим столбец is_24/7 с обозначением, что заведение работает ежедневно и круглосуточно (24/7): логическое значение True — если заведение работает ежедневно и круглосуточно; логическое значение False — в противоположном случае.
# добавим столбец и выведем результаты на экран
df['is_24/7'] = df['hours'].str.contains('ежедневно, круглосуточно')
df.head()
| name | category | address | district | hours | lat | lng | rating | price | avg_bill | middle_avg_bill | middle_coffee_cup | chain | seats | street | is_24/7 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | wowфли | кафе | москва, улица дыбенко, 7/1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.878494 | 37.478860 | 5.0 | NaN | NaN | NaN | NaN | 0 | NaN | улица дыбенко | False |
| 1 | четыре комнаты | ресторан | москва, улица дыбенко, 36, корп. 1 | Северный административный округ | ежедневно, 10:00–22:00 | 55.875801 | 37.484479 | 4.5 | выше среднего | Средний счёт:1500–1600 ₽ | 1550.0 | NaN | 0 | 4.0 | улица дыбенко | False |
| 2 | хазри | кафе | москва, клязьминская улица, 15 | Северный административный округ | пн-чт 11:00–02:00; пт,сб 11:00–05:00; вс 11:00... | 55.889146 | 37.525901 | 4.6 | средние | Средний счёт:от 1000 ₽ | 1000.0 | NaN | 0 | 45.0 | клязьминская улица | False |
| 3 | dormouse coffee shop | кофейня | москва, улица маршала федоренко, 12 | Северный административный округ | ежедневно, 09:00–22:00 | 55.881608 | 37.488860 | 5.0 | NaN | Цена чашки капучино:155–185 ₽ | NaN | 170.0 | 0 | NaN | улица маршала федоренко | False |
| 4 | иль марко | пиццерия | москва, правобережная улица, 1б | Северный административный округ | ежедневно, 10:00–22:00 | 55.881166 | 37.449357 | 5.0 | средние | Средний счёт:400–600 ₽ | 500.0 | NaN | 1 | 148.0 | правобережная улица | False |
Категории заведений
Посмотрим какие категории заведений представлены в данных. Исследуем количество объектов общественного питания по категориям:
Построим визуализации.
# расчитаем количество заведений по категориям
category_name = df.groupby('category')['name'].count().reset_index()
category_name.columns = ['category', 'count']
category_name
| category | count | |
|---|---|---|
| 0 | бар,паб | 755 |
| 1 | булочная | 255 |
| 2 | быстрое питание | 602 |
| 3 | кафе | 2364 |
| 4 | кофейня | 1401 |
| 5 | пиццерия | 629 |
| 6 | ресторан | 2031 |
| 7 | столовая | 313 |
# построим круговую диаграмму для распределения заведений по категориям в процентах
go.Figure(data=[go.Pie(labels=category_name['category'],
values= category_name['count'])],
layout = go.Layout(title=go.layout.Title(text='Категории заведений общественного питания г. Масква')))
# построим график для отображение количества заведений по категориям
fig = px.bar(category_name,
x='category',
y='count',
text = 'count',
#color = 'count',
title = 'Количество заведений общественного питания по категориям'
)
fig.update_layout(xaxis_title = 'Категории заведений',
yaxis_title = 'Количество заведений',
xaxis={'categoryorder':'total descending'})
fig.show()
Вывод:
По количеству заведений лидируют кафе с численностью 2362 заведений., немного меньше ресторанов - 2030 шт. На последнем месте булочная - 255 шт.
Кафе и рестораны составляют больше половины от всех заведений. Кофейни, бары/пабы и пиццерии составляют 1/3 от общего числа заведений. На булочные, столовые и быстрое питание приходится около 14%.
Исследование количества посадочных мест в местах по категориям.
Исследуем количество посадочных мест в местах по категориям: рестораны, кофейни, пиццерии, бары и так далее. Построим визуализации.
# выведем общую информацию count, mean, std, min, max о заведениях
df.groupby('category')['seats'].describe().round(2).reset_index()
| category | count | mean | std | min | 25% | 50% | 75% | max | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | бар,паб | 458.0 | 109.04 | 93.20 | 0.0 | 47.25 | 80.0 | 140.00 | 480.0 |
| 1 | булочная | 147.0 | 85.74 | 87.35 | 0.0 | 25.00 | 50.0 | 120.00 | 478.0 |
| 2 | быстрое питание | 348.0 | 96.19 | 94.02 | 0.0 | 27.25 | 65.0 | 136.25 | 500.0 |
| 3 | кафе | 1204.0 | 89.57 | 88.93 | 0.0 | 35.00 | 60.0 | 106.00 | 500.0 |
| 4 | кофейня | 739.0 | 100.43 | 90.99 | 0.0 | 40.00 | 78.0 | 140.00 | 500.0 |
| 5 | пиццерия | 423.0 | 87.91 | 85.48 | 0.0 | 30.00 | 52.0 | 120.00 | 455.0 |
| 6 | ресторан | 1258.0 | 114.96 | 96.94 | 0.0 | 48.00 | 86.0 | 150.00 | 500.0 |
| 7 | столовая | 162.0 | 89.72 | 77.27 | 0.0 | 40.00 | 75.0 | 115.00 | 428.0 |
# построим график для отображения среднего количества мест в заведениях питания
seats_mean = df[df['seats'] !=0].groupby('category')['seats'].mean().reset_index().sort_values(by='seats', ascending=False)
f, ax = plt.subplots(figsize=(16, 7))
ax = sns.barplot(data = seats_mean, x = 'category', y = 'seats')
ax.set_xlabel('Тип заведения')
ax.set_ylabel('Количество мест')
ax.set_title('Среднее количество мест в заведениях в Москве')
plt.xticks(rotation=45)
plt.show()
# визуализируем медианное количество мест в заведениях
seats_median = df[df['seats'] != 0].groupby('category')['seats'].median().reset_index().sort_values(by='seats', ascending=False).round(2)
f, ax = plt.subplots(figsize=(16, 7))
ax = sns.barplot(data=seats_median, x='category', y='seats')
ax.set_xlabel('Тип заведения')
ax.set_ylabel('Количество мест')
ax.set_title('Медианное количество мест в заведениях Москвы')
plt.xticks(rotation=45)
plt.show()
order = df.groupby(["category"])["seats"].median().sort_values(ascending=False).index
# построим график для распределения количества мест по типам заведений
plt.figure(figsize=(15, 7))
ax = sns.boxplot(data=df, x='seats', y='category', palette="Set2", order = order)
plt.title('Распределение количества посадочных мест по типам заведений')
plt.xlabel('Количество мест')
plt.ylabel('Тип заведений')
plt.show()
Вывод:
Больше всего посадочных мест в ресторанах, что логично. Люди празднуют свадьбы, проводят корпоративы и т. д. Далее идут заведения быстрого питания, т. к. они больше всего распространены в Москве. Кафе и столовые располагают для своих гостей меньшим количеством мест.
Cоотношение сетевых и несетевых заведений в датасете
chain = df['chain'].value_counts().reset_index()
chain.columns = ['chain', 'count']
chain
| chain | count | |
|---|---|---|
| 0 | 0 | 5171 |
| 1 | 1 | 3179 |
# визуализируем результаты на диаграмме
fig = go.Figure(data=[go.Pie(labels=['несетевые', 'сетевые'], values=chain['count'])], layout=go.Layout(
title=go.layout.Title(text="Соотношение сетевых и несетевых заведений")))
fig.show()
Вывод:
По графику видно, что почти 62% заведений в Москве - несетевые.
Посмотрим какие категории заведений чаще являются сетевыми
# посчитаем рапсределение заведение по типу сетевые и нет
chain_establishments = df.groupby(['category', 'chain'])['name'].count().reset_index()
chain_establishments.columns = ['type', 'chain', 'count']
chain_establishments['chain'] = chain_establishments['chain'].astype(object)
chain_establishments = chain_establishments.sort_values(['count', 'chain'])
chain_establishments
| type | chain | count | |
|---|---|---|---|
| 15 | столовая | 1 | 87 |
| 2 | булочная | 0 | 99 |
| 3 | булочная | 1 | 156 |
| 1 | бар,паб | 1 | 168 |
| 14 | столовая | 0 | 226 |
| 5 | быстрое питание | 1 | 231 |
| 10 | пиццерия | 0 | 301 |
| 11 | пиццерия | 1 | 328 |
| 4 | быстрое питание | 0 | 371 |
| 0 | бар,паб | 0 | 587 |
| 8 | кофейня | 0 | 688 |
| 9 | кофейня | 1 | 713 |
| 13 | ресторан | 1 | 722 |
| 7 | кафе | 1 | 774 |
| 12 | ресторан | 0 | 1309 |
| 6 | кафе | 0 | 1590 |
# построим график для визуализации расчетов
fig = px.bar(chain_establishments,
x='count',
y='type',
text= 'count',
color='chain',
category_orders={"chain":['Сетевой', 'Несетевой']},
color_discrete_sequence=["#E69F00", "#56B4E9"],
height=500,
width=700
)
fig.update_layout(title = 'Соотношение сетевых и несетевых заведений',
xaxis_title = 'Количество заведений',
yaxis_title = 'Категории')
fig.show()
chain_grouped = df[df['chain'] == 1].groupby(['category'])['name'].count()\
.reset_index().sort_values(by='name', ascending=False)
notChain_grouped = df[df['chain'] == 0].groupby(['category'])['name'].count()\
.reset_index().sort_values(by='name', ascending=False)
# инициализация фигуры
fig = go.Figure()
# добавление барчарта методом Bar()
fig.add_trace(go.Bar(x=chain_grouped.category, y=chain_grouped.name,\
name='Сетевое', marker={'color': '#E69F00', 'opacity': 0.7}))
fig.add_trace(go.Bar(x=notChain_grouped.category, y=notChain_grouped.name,\
name='Несетевое', marker={'color': '#56B4E9', 'opacity': 0.7}))
# добавление соответствующих подписей и меток
fig.update_layout(title='Соотношение сетевых и несетевых заведений по количеству',\
xaxis_title='Категория', yaxis_title='Количество объектов', legend=dict(y=0.95, x=0.84))
fig.update_traces(hoverinfo='all', hovertemplate='Количество объектов: %{y}')
# отображение фигуры
fig.show()
Вывод:
По графику видно, что большинство за несетевыми заведениями. Но есть пара исключений:
Топ-15 популярных сетей в Москве
# сгруппируем данные по названиям заведений и найдем топ-15 популярных сетей в Москве
data_chain = df[df['chain'] == 1]
top_15 = data_chain.groupby('name').agg({'rating': 'median', 'category' : pd.Series.mode, 'district' : 'count'})
top_15 = top_15.rename(columns={'district':'count'})
top_15 = top_15.sort_values('count', ascending = False).reset_index().head(15)
top_15
| name | rating | category | count | |
|---|---|---|---|---|
| 0 | шоколадница | 4.20 | кофейня | 118 |
| 1 | домино'с пицца | 4.20 | пиццерия | 76 |
| 2 | додо пицца | 4.30 | пиццерия | 74 |
| 3 | one price coffee | 4.20 | кофейня | 70 |
| 4 | яндекс лавка | 4.00 | ресторан | 68 |
| 5 | cofix | 4.10 | кофейня | 65 |
| 6 | prime | 4.20 | ресторан | 50 |
| 7 | хинкальная | 4.40 | кафе | 44 |
| 8 | кофепорт | 4.20 | кофейня | 42 |
| 9 | кулинарная лавка братьев караваевых | 4.40 | кафе | 38 |
| 10 | теремок | 4.10 | ресторан | 38 |
| 11 | чайхана | 4.10 | кафе | 37 |
| 12 | cofefest | 4.05 | кофейня | 32 |
| 13 | буханка | 4.40 | булочная | 32 |
| 14 | му-му | 4.30 | кафе | 27 |
# построим график для визуализации произведеннйх расчетов
fig = px.bar(top_15,
x='count',
y='name',
text='count',
color='name',
height=500,
width=1000
)
fig.update_layout(title = 'Топ-15 популярных сетей Москвы',
xaxis_title = 'Количество заведений',
yaxis_title = 'Название заведений',
showlegend = False)
fig.show()
Вывод:
Из графика видно, что самая популярная сеть - Шоколадница. Далее идут заведения продающих пиццу. Сеть Му-Му замыкает топ.
print('Всего заведений в Топ-15:', top_15['count'].sum())
Всего заведений в Топ-15: 811
fig = px.bar(top_15,
x='count',
y='category',
color='category',
text = 'name'
)
fig.update_layout(title='Количество заведений по категориям из топ-15 популярных сетей Москвы',
xaxis_title='Количество заведений',
yaxis_title='Название категорий',
yaxis={'categoryorder':'total ascending'},
width=900,
height=500
)
fig.show()
# расчитаем рейтинг заведений в зависимости от района
district_chain = data_chain.groupby(['district', 'category', 'name']).agg({'rating' : 'median', 'address' : 'count'})
district_chain = district_chain.sort_values('address', ascending = False).reset_index()
district_chain = district_chain.rename(columns={'address':'count'})
district_chain = district_chain[district_chain['name'].isin(top_15['name'])]
district_chain.head()
| district | category | name | rating | count | |
|---|---|---|---|---|---|
| 0 | Центральный административный округ | кофейня | шоколадница | 4.20 | 38 |
| 1 | Центральный административный округ | ресторан | prime | 4.20 | 36 |
| 2 | Центральный административный округ | кафе | кулинарная лавка братьев караваевых | 4.40 | 32 |
| 3 | Центральный административный округ | кофейня | cofix | 4.20 | 20 |
| 4 | Западный административный округ | кофейня | шоколадница | 4.15 | 16 |
# построим график для вызуализации проведенных расчетов
fig = px.bar(district_chain,
x='count',
y='district',
color='category'
)
fig.update_layout(title='Количество заведений каждой категории по районам',
xaxis_title='Количество заведений',
yaxis_title='Название района',
yaxis={'categoryorder':'total ascending'},
width=1000,
height=700
)
fig.show()
# шаг 1 выделим датасет только с популярными заведениями
top15_count = df[df['name'].isin(top_15['name'])].groupby(['district']).agg({'name' : 'count'}).reset_index()
top15_count = top15_count.rename(columns={'name':'count'})
top15_count
| district | count | |
|---|---|---|
| 0 | Восточный административный округ | 80 |
| 1 | Западный административный округ | 85 |
| 2 | Северный административный округ | 87 |
| 3 | Северо-Восточный административный округ | 85 |
| 4 | Северо-Западный административный округ | 46 |
| 5 | Центральный административный округ | 215 |
| 6 | Юго-Восточный административный округ | 56 |
| 7 | Юго-Западный административный округ | 70 |
| 8 | Южный административный округ | 89 |
# шаг 2 подсчитаем общее количество заведений по округам
district_count = df.groupby(['district']).agg({'name' : 'count'})
district_count = district_count.sort_values('name', ascending = False).reset_index()
district_count = district_count.rename(columns={'name':'total'})
district_count.head(15)
| district | total | |
|---|---|---|
| 0 | Центральный административный округ | 2242 |
| 1 | Северо-Восточный административный округ | 891 |
| 2 | Южный административный округ | 891 |
| 3 | Северный административный округ | 877 |
| 4 | Западный административный округ | 831 |
| 5 | Восточный административный округ | 789 |
| 6 | Юго-Восточный административный округ | 714 |
| 7 | Юго-Западный административный округ | 706 |
| 8 | Северо-Западный административный округ | 409 |
# объединим в одну таблицу
district_merge = top15_count.merge(district_count, how='left', on='district')
district_merge['percent'] = round(district_merge['count']/district_merge['total']*100, 2)
district_merge = district_merge.sort_values('percent', ascending=False)
district_merge
| district | count | total | percent | |
|---|---|---|---|---|
| 4 | Северо-Западный административный округ | 46 | 409 | 11.25 |
| 1 | Западный административный округ | 85 | 831 | 10.23 |
| 0 | Восточный административный округ | 80 | 789 | 10.14 |
| 8 | Южный административный округ | 89 | 891 | 9.99 |
| 2 | Северный административный округ | 87 | 877 | 9.92 |
| 7 | Юго-Западный административный округ | 70 | 706 | 9.92 |
| 5 | Центральный административный округ | 215 | 2242 | 9.59 |
| 3 | Северо-Восточный административный округ | 85 | 891 | 9.54 |
| 6 | Юго-Восточный административный округ | 56 | 714 | 7.84 |
# построи грайик для наглядности
f, ax = plt.subplots(figsize=(16, 7))
ax = sns.barplot(data=district_merge, y="district", x="percent")
ax.set_xlabel('Процент популярных заведений')
ax.set_ylabel('Название района')
ax.set_title('Процент популярных заведений от района')
plt.show()
Вывод:
Всего заведений в топ-15 - 811 шт. По графику наглядно видно, что большая часть приходится на кофейни. У ресторанов, пиццерий и кафе примерно равное количество.
Из топ-15 больше всего заведений находятся в Центральном админинстративном округе. В основном это кафе, кофейни и рестораны. Примерно одинаковые показатели у Южного, Северного, Северо-Восточного и Западного админинстративных округов. Здесь приоритеты меняются в сторону категории быстрого питания. Меньше всего заведений из топ-15 в Северо-Западном админинстративном округе.
Административные районы Москвы
Посмотрим какие административные районы Москвы присутствуют в датасете. Отобразим общее количество заведений и количество заведений каждой категории по районам.
print('Общее количество заведений в датасете:', df['name'].count())
Общее количество заведений в датасете: 8350
# расчитаем распределение заведений по округам
district_chain_data = df.groupby(['district', 'category']).agg({'rating': 'median', 'name': 'count'})
district_chain_data = district_chain_data.sort_values('rating', ascending=False).reset_index()
district_chain_data = district_chain_data.rename(columns={'name': 'count'})
district_chain_data.head()
| district | category | rating | count | |
|---|---|---|---|---|
| 0 | Центральный административный округ | бар,паб | 4.50 | 364 |
| 1 | Западный административный округ | бар,паб | 4.45 | 46 |
| 2 | Северо-Западный административный округ | бар,паб | 4.40 | 23 |
| 3 | Северный административный округ | бар,паб | 4.40 | 66 |
| 4 | Центральный административный округ | ресторан | 4.40 | 670 |
# визуализируем расчеты
fig = px.bar(district_chain_data,
x='count',
y='district',
color='category'
)
fig.update_layout(title = 'Количество заведений каждой категории по районам Москвы',
xaxis_title = 'Количество заведений',
yaxis_title = 'Название района',
yaxis={'categoryorder': 'total ascending'}
)
fig.show()
# выведем уникальные названия административных районов Москвы
print(f'Административные районы Москвы в датасете:', district_chain['district'].unique())
Административные районы Москвы в датасете: ['Центральный административный округ' 'Западный административный округ' 'Северный административный округ' 'Восточный административный округ' 'Южный административный округ' 'Северо-Восточный административный округ' 'Юго-Западный административный округ' 'Юго-Восточный административный округ' 'Северо-Западный административный округ']
Вывод:
Количество районов - 9. По графику видно, что больше всего заведений располагается в Центральном административном округе. В нем преобладают кафе, кофейни и рестораны. Кафе распределены равномерно по всем округам. Столовых меньше всего по всем округам.
Визуализируем распределение средних рейтингов по категориям заведений.
# расчитаем средний рейтинг по типам заведений
category_rating = df.groupby('category').agg({'rating': 'mean'}).round(2).sort_values('rating', ascending=False).reset_index()
category_rating
| category | rating | |
|---|---|---|
| 0 | бар,паб | 4.39 |
| 1 | пиццерия | 4.30 |
| 2 | ресторан | 4.29 |
| 3 | кофейня | 4.28 |
| 4 | булочная | 4.27 |
| 5 | столовая | 4.21 |
| 6 | кафе | 4.12 |
| 7 | быстрое питание | 4.05 |
# визуализируем результаты расчтеов для наглядности
fig = px.bar(category_rating,
x='rating',
y='category',
text='rating',
color='category'
)
fig.update_layout(title='Распределение средних рейтингов по категориям заведений',
xaxis_title='Рейтинг',
yaxis_title='Название категорий')
fig.update_xaxes(range=[4, 4.5])
fig.show()
Вывод:
Из графика видно, что у категорий бар/паб наивысший рейтинг 4.39. У пиццерий, ресторанов, кофеин и булочных примерно одинаковый рейтинг. Самый маленький рейтинг у ресторанов быстрого питания. Также видно, что по все заведения имеют рейтинг выше 4, что достаточно хорошо.
Построим фоновую картограмму (хороплет) со средним рейтингом заведений каждого района. Границы районов Москвы, которые встречаются в датасете, хранятся в файле admin_level_geomap.geojson
rating_df = df.groupby('district', as_index=False)['rating'].agg('mean').round(2)
rating_df
| district | rating | |
|---|---|---|
| 0 | Восточный административный округ | 4.17 |
| 1 | Западный административный округ | 4.18 |
| 2 | Северный административный округ | 4.24 |
| 3 | Северо-Восточный административный округ | 4.15 |
| 4 | Северо-Западный административный округ | 4.21 |
| 5 | Центральный административный округ | 4.38 |
| 6 | Юго-Восточный административный округ | 4.10 |
| 7 | Юго-Западный административный округ | 4.17 |
| 8 | Южный административный округ | 4.18 |
# загружаем JSON-файл с границами округов Москвы
state_geo = 'https://code.s3.yandex.net/data-analyst/admin_level_geomap.geojson'
# moscow_lat - широта центра Москвы, moscow_lng - долгота центра Москвы
moscow_lat, moscow_lng = 55.751244, 37.618423
# Создаем карту Мск
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=rating_df,
columns=['district', 'rating'],
key_on='feature.name',
#fill_color='YlGn',
#fill_opacity=0.8,
legend_name='Средний рейтинг заведений по районам',
).add_to(m)
# вывод карты
m
Вывод:
По данным видно, что самый высокий рейтинг в заведениях в Центральном админинстративном округе - 4.38. Самый низкий - в Юго-Восточном админинстратвном округе - 4.1.
# moscow_lat - широта центра Москвы, moscow_lng - долгота центра Москвы
moscow_lat, moscow_lng = 55.751244, 37.618423
# создаём карту Москвы
m = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(m)
# функция, принимающая строку датафрейма, создает маркер в текущей точке и
# добавляет его в кластер marker_cluster
def create_cluster(row):
Marker(
[row['lat'], row['lng']],
popup=f"{row['name']} {row['rating']}",
).add_to(marker_cluster)
# к каждой строке датафрейма применяем функцию
df.apply(create_cluster, axis=1)
# выводим карту
m
Вывод:
Видно, что основная масса заведений располагаются в центре Москвы.
Найдем топ-15 улиц по количеству заведений. Построим график распределения количества заведений и их категорий по этим улицам.
# подсчитаем распределение завдений по улицам Москвы
msc_streets = df[df['street'].notnull()]
top15_streets = msc_streets['street'].value_counts().reset_index().head(15)
top15_streets.columns = ['street', 'count']
top15_streets
| street | count | |
|---|---|---|
| 0 | проспект мира | 184 |
| 1 | профсоюзная улица | 122 |
| 2 | ленинский проспект | 107 |
| 3 | проспект вернадского | 97 |
| 4 | дмитровское шоссе | 88 |
| 5 | каширское шоссе | 77 |
| 6 | варшавское шоссе | 76 |
| 7 | ленинградский проспект | 72 |
| 8 | ленинградское шоссе | 70 |
| 9 | мкад | 65 |
| 10 | люблинская улица | 60 |
| 11 | улица вавилова | 55 |
| 12 | кутузовский проспект | 53 |
| 13 | пятницкая улица | 48 |
| 14 | алтуфьевское шоссе | 47 |
# таблица со столбцами - улица - категоория - количество
streets_category = df.groupby(['street', 'category'])['name'].count().reset_index()
streets_category.columns = ['street', 'category', 'count']
streets_category.sort_values('count', ascending=False)
| street | category | count | |
|---|---|---|---|
| 2136 | проспект мира | кафе | 53 |
| 2139 | проспект мира | ресторан | 45 |
| 1519 | мкад | кафе | 45 |
| 2137 | проспект мира | кофейня | 36 |
| 2148 | профсоюзная улица | кафе | 35 |
| ... | ... | ... | ... |
| 1534 | молодёжная улица | бар,паб | 1 |
| 1535 | молодёжная улица | кофейня | 1 |
| 1537 | монтажная улица | кофейня | 1 |
| 1538 | монтажная улица | ресторан | 1 |
| 3818 | № 7 | кафе | 1 |
3819 rows × 3 columns
# улицы из топ-15
streets_top15_category = streets_category[streets_category['street'].isin(top15_streets['street'])]
streets_top15_category
| street | category | count | |
|---|---|---|---|
| 320 | алтуфьевское шоссе | бар,паб | 5 |
| 321 | алтуфьевское шоссе | булочная | 1 |
| 322 | алтуфьевское шоссе | быстрое питание | 5 |
| 323 | алтуфьевское шоссе | кафе | 13 |
| 324 | алтуфьевское шоссе | кофейня | 9 |
| ... | ... | ... | ... |
| 2794 | улица вавилова | быстрое питание | 11 |
| 2795 | улица вавилова | кафе | 15 |
| 2796 | улица вавилова | кофейня | 10 |
| 2797 | улица вавилова | пиццерия | 3 |
| 2798 | улица вавилова | ресторан | 12 |
113 rows × 3 columns
# построим график для отображения расчетов
fig = px.bar(streets_top15_category,
x='count',
y='street',
color='category'
)
fig.update_layout(title='Количество заведений каждой категории по районам',
xaxis_title='Количество заведений',
yaxis_title='Название улиц',
yaxis={'categoryorder':'total ascending'}
)
fig.show()
Вывод:
Из графика можно заметить, что больше всего заведений на проспекте Мира. Преобладают кафе и рестораны. Далее идет Профсоюзная улица. По заведениям ситуация схожая.
Найдем улицы, на которых находится только один объект общепита
# выполним расчеты
streets_with_one_cafe = df['street'].value_counts().reset_index()
streets_with_one_cafe.columns = ['street', 'count_cafe']
streets_with_one_cafe = streets_with_one_cafe[streets_with_one_cafe['count_cafe']==1]
streets_with_one_cafe
| street | count_cafe | |
|---|---|---|
| 990 | андреевский пешеходный мост | 1 |
| 991 | загорьевский проезд | 1 |
| 992 | 2-й верхний михайловский проезд | 1 |
| 993 | новороссийская улица | 1 |
| 994 | проезд донелайтиса | 1 |
| ... | ... | ... |
| 1442 | улица маршала соколовского | 1 |
| 1443 | малый казённый переулок | 1 |
| 1444 | улица максимова | 1 |
| 1445 | басманный тупик | 1 |
| 1446 | чонгарский бульвар | 1 |
457 rows × 2 columns
# посмотрим к каким категориям относятся заведения
streets_category_new = streets_category[streets_category['street'].isin(streets_with_one_cafe['street'])]
streets_category_new = streets_category_new.groupby('category')['street'].count().sort_values(ascending=False)
streets_category_new
category кафе 159 ресторан 93 кофейня 84 бар,паб 39 столовая 36 быстрое питание 23 пиццерия 15 булочная 8 Name: street, dtype: int64
Вывод:
Видно, что 458 улиц имеют только одно заведение. Больше всего - это кафе (160).
Значения средних чеков заведений хранятся в столбце middle_avg_bill. Эти числа показывают примерную стоимость заказа, которая чаще всего выражена диапазоном.
# подсчитаем медианное значение среднего чека
median_bill = df.groupby('district')['middle_avg_bill'].median().reset_index()
median_bill
| district | middle_avg_bill | |
|---|---|---|
| 0 | Восточный административный округ | 550.0 |
| 1 | Западный административный округ | 1000.0 |
| 2 | Северный административный округ | 650.0 |
| 3 | Северо-Восточный административный округ | 500.0 |
| 4 | Северо-Западный административный округ | 700.0 |
| 5 | Центральный административный округ | 1000.0 |
| 6 | Юго-Восточный административный округ | 450.0 |
| 7 | Юго-Западный административный округ | 600.0 |
| 8 | Южный административный округ | 500.0 |
# создаем карту Москвы
mm = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# хороплет
Choropleth(
geo_data=state_geo,
data = median_bill,
columns = ['district', 'middle_avg_bill'],
key_on = 'feature.name',
legend_name = 'Средний чек заведений по районам',
).add_to(mm)
mm
Вывод:
По данным выше мы видим, что самый высокий средний чек в Центральном и Западном округах. Самый низкий в Юго-Восточном административном округе. Из таблицы выше видно, что средний чек округов вокруг центрального в среднем отличается в 1,5 - 2 раза.
Был проведен анализ заведений общественного питания Москвы. По количеству заведений лидируют кафе с количеством 2364 (28.3%), чуть меньше рестораны - 2031 (24.3%). Категория булочные на последнем месте - 255 (3.05%).
В процессе проведения исседования выявлено, что больше всего посадочных мест предоставляют рестораны. Что логично, т.к. люди обычно празднуют там события с большой численностью людей (свадьбы, корпоративы и т. д.). Далее идут заведения быстрого питания, т.к. они больше всего распространены в Москве.
Для кафе и столовых посадочных мест меньше всего.
В Москве 61.9% мест являются несетевыми. По графику видно, что большинство за несетевыми заведениями. Но есть пара исключений:
кофейня - количество сетевых немного больше - 713/688
пиццерия - сетевых заведений больше - 328/301
булочная - сетевых заметно больше - 156/99
Самая популярная сеть в Москве - Шоколадница. На втором и третьем месте расположились известные пиццерии.
Всего заведений в топ-15 - 811. По графику наглядно видно, что большая часть приходится на кофейни. У ресторанов, пиццерий и кафе примерно равное количество.
Из топ-15 больше всего заведений находятся в Центральном админинстративном округе. В основном это кафе, кофейни и рестораны. Примерно одинаковые показатели у Южного, Северного, Северо-Восточного и Западного админинстративных округов. Здесь приоритеты меняются в сторону категории быстрого питания. Меньше всего заведений из топ-15 в Северо-Западном админинстративном округе.
Количество районов - 9. По графику видно, что больше всего заведений располагается в Центральном админинстративном округе. В нем преобладают кафе, кофейни и рестораны. Кафе распределены равномерно по всем округам. Столовых меньше всего по всем округам.
У категорий бар/паб наивысший рейтинг 4.39. У пиццерий, ресторанов, кофеин и булочных примерно одинаковый рейтинг. Самый маленький рейтинг у ресторанов быстрого питания. Также видно, что по все заведениям рейтинг выше 4, что достаточно хорошо.
Самый высокий рейтинг в заведениях в Центральном админинстративном округе - 4.38. Самый низкий - в Юго-Восточном админинстратвном округе - 4.1.
Основная масса заведений располагаются в центре Москвы.
Больше всего заведений на проспекте Мира. Преобладают кафе и рестораны. Далее идет Профсоюзная улица. По заведениям ситуация схожая.
458 улиц имеют только одно заведение. Больше всего - это кафе (160).
Самый высокий средний чек в Центральном и Западном округах. Самый маленький в Юго-Восточном административном округе. Из таблицы выше видно, что средний чек округов вокруг центрального в среднем отличается в 1,5 - 2 раза.
Ответим на следующие вопросы:
coffee_data = df[df['category'] == 'кофейня']
print('Количество кофеин:', len(coffee_data))
Количество кофеин: 1401
# создаем карту
mmm = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаем пустой кластер и добавляем его на карту
marker_cluster = MarkerCluster().add_to(mmm)
# функция, которая принимает строку датафрейма,
# создаёт маркер в текущей точке и добавляет его в кластер marker_cluster
def create_clusters(row):
Marker(
[row['lat'], row['lng']],
popup=f"{row['name']} {row['rating']}",
).add_to(marker_cluster)
# применяем функцию create_clusters() к каждой строке датафрейма
coffee_data.apply(create_clusters, axis=1)
# выводим карту
mmm
Вывод:
Всего 1401 заведение в категории кофейня. Большая часть заведений располагается в Центральном районе. Меньше всего заведений - в восточной и южной частях города.
coffee_24_7 = coffee_data[coffee_data['is_24/7'] == True]
print('Количество круглосуточных кофеен:', len(coffee_24_7))
Количество круглосуточных кофеен: 59
# создаём карту Москвы
mmmm = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём пустой кластер, добавляем его на карту
marker_cluster = MarkerCluster().add_to(mmmm)
# пишем функцию, которая принимает строку датафрейма,
# создаёт маркер в текущей точке и добавляет его в кластер marker_cluster
def create_clusters(row):
Marker(
[row['lat'], row['lng']],
popup=f"{row['name']} {row['rating']}",
).add_to(marker_cluster)
# применяем функцию create_clusters() к каждой строке датафрейма
coffee_24_7.apply(create_clusters, axis=1)
# выводим карту
mmmm
Вывод:
Количество круглосуточных кофеен - 59. По карте видно, что большинство находится в центре города. Это логично, т. к. ночью люди любят гулять по центру и спрос на кофе присутствует. В других районах это не распространено.
# подсчитаем рейтинг кофеен
coffee_rating = coffee_data.groupby('district', as_index=False)['rating'].agg('mean').round(2).sort_values('rating', ascending=False)
coffee_rating
| district | rating | |
|---|---|---|
| 5 | Центральный административный округ | 4.34 |
| 4 | Северо-Западный административный округ | 4.33 |
| 2 | Северный административный округ | 4.29 |
| 0 | Восточный административный округ | 4.28 |
| 7 | Юго-Западный административный округ | 4.28 |
| 6 | Юго-Восточный административный округ | 4.23 |
| 8 | Южный административный округ | 4.23 |
| 3 | Северо-Восточный административный округ | 4.22 |
| 1 | Западный административный округ | 4.19 |
# создаём карту
mmmmm = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карт
Choropleth(
geo_data=state_geo,
data=coffee_rating,
columns=['district', 'rating'],
key_on='feature.name',
legend_name='Средний рейтинг заведений по районам',
).add_to(mmmmm)
# выводим карту
mmmmm
Вывод:
Самый высокий рейтинг в округах: Центральный административный округ и Северо-Западный административный округ. Самый низкий рейтинг у Западного административного округа
# рассчитаем стоимость чашки каппучино
cup_of_coffee = coffee_data.groupby('district', as_index=False)['middle_coffee_cup'].agg('mean').round(2).sort_values('middle_coffee_cup',
ascending=False)
display(cup_of_coffee)
coffee_price = round(cup_of_coffee['middle_coffee_cup'].mean(), 2)
print(f'Средняя стоимость чашки кофе в Москве:', coffee_price)
| district | middle_coffee_cup | |
|---|---|---|
| 1 | Западный административный округ | 188.56 |
| 5 | Центральный административный округ | 187.52 |
| 7 | Юго-Западный административный округ | 184.18 |
| 0 | Восточный административный округ | 174.02 |
| 2 | Северный административный округ | 166.34 |
| 4 | Северо-Западный административный округ | 165.52 |
| 3 | Северо-Восточный административный округ | 165.33 |
| 8 | Южный административный округ | 158.49 |
| 6 | Юго-Восточный административный округ | 151.09 |
Средняя стоимость чашки кофе в Москве: 171.23
m_6 = Map(location=[moscow_lat, moscow_lng], zoom_start=10)
# создаём хороплет с помощью конструктора Choropleth и добавляем его на карту
Choropleth(
geo_data=state_geo,
data=cup_of_coffee,
columns=['district', 'middle_coffee_cup'],
key_on='feature.name',
legend_name='Средняя цена чашки кофе по районам',
).add_to(m_6)
# выводим карту
m_6
Вывод:
Самый дорогой кофе в Западном (188.56) и Центральном округах (187.52). Средняя стоимость чашки кофе для Москвы - 171.23.
Рекомендации:
При открытии кофейни стоит рассмотреть следующие районы: Центральный, Западный и Юго-Западный, т. к. в этих районах высокая стоимость чашки кофе. Это позволит немного снизить цену на старте продаж без существенных потерь для привлечения клиентов. Если рассматривать открытие в Центральном районе, то лучше ориентироваться на круглосуточный режим работы, т.к. это самый оживленный район в ночное время. На Западном и Юго-Западном направлении круглосуточных точек мало.
Возможно, стоит попробовать поэкспериментировать с круглосуточным режимом работы и посмотреть будут ли в ночное время посетители. Если посетители будут, то это будет большим плюсом для бизнеса, т. к. в этих районах конкурентов в ночное время мало. Также можно отметить, что Западный район привлекателен для открытия тем, что в нем самый низкий средний рейтинг у кофеин, что может стать преимуществом при открытии нового заведения и при успешном старте можно стать самым популярным заведением в данном районе.
Как мне кажется, открытие заведений с хорошим рейтингом из Центрального района в других районах (по франшизе) - также хороший вариант, т. к. в Центральном районе очень большая проходимость и количество отзывов и рейтинг Центрального района может хорошо повлиять на заведение и в других районах.